﻿using System;
using Csla;
using System.ComponentModel.DataAnnotations;
using BizControl.DalLinq;
using BizControl.Library.BusinessObjects.People;
using BizControl.Library.BusinessObjects.Activities;
using Csla.Rules.CommonRules;
using Csla.Data;
using System.Data.Linq;
using BizControl.Library.Security;
using BizControl.Resources;
using BizControl.Library.BusinessObjects.BusinessRoles;
using System.Security;
using Csla.Rules;

namespace BizControl.Library.BusinessObjects.Participants
{
  [Serializable()]
  public class ParticipantBO : BusinessBase<ParticipantBO>, ISupportEmptyValue
  {
    #region Business Methods

    private static readonly PropertyInfo<Guid> ParticipantIdProperty = RegisterProperty<Guid>(c => c.ParticipantId);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ParticipantParticipantIdRequiredPropertyError")]
    public Guid ParticipantId
    {
      get { return GetProperty(ParticipantIdProperty); }
      private set { SetProperty(ParticipantIdProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> PersonProperty = RegisterProperty<PersonInfo>(c => c.Person);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ParticipantPersonRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ParticipantPersonProperty")]
    public PersonInfo Person
    {
      get { return GetProperty(PersonProperty); }
      set { SetProperty(PersonProperty, value); }
    }

    private static readonly PropertyInfo<Guid?> ActivityProperty = RegisterProperty<Guid?>(c => c.ActivityId);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"ActivityRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"ActivityIdProperty")]
    internal Guid? ActivityId
    {
      get { return GetProperty(ActivityProperty); }
      set { SetProperty(ActivityProperty, value); }
    }

    private static readonly PropertyInfo<BusinessRoleInfo> RoleProperty = RegisterProperty<BusinessRoleInfo>(c => c.Role);
    [Display(ResourceType = typeof(PropertiesNames), Name = @"RoleProperty")]
    public BusinessRoleInfo Role
    {
      get { return GetProperty(RoleProperty); }
      set { SetProperty(RoleProperty, value); }
    }

    #region Common properties

    private static readonly PropertyInfo<bool> IsDeletedFieldProperty = RegisterProperty<bool>(c => c.IsDeletedField, null, false);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"IsDeletedRequiredError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"IsDeletedFieldProperty")]
    public bool IsDeletedField
    {
      get { return GetProperty(IsDeletedFieldProperty); }
      private set { SetProperty(IsDeletedFieldProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> CreatedByProperty = RegisterProperty<PersonInfo>(c => c.CreatedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedByProperty")]
    public PersonInfo CreatedBy
    {
      get { return GetProperty(CreatedByProperty); }
      private set { SetProperty(CreatedByProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> CreatedOnProperty = RegisterProperty<DateTime>(c => c.CreatedOn);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"CreatedOnRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"CreatedOnProperty")]
    public DateTime CreatedOn
    {
      get { return GetProperty(CreatedOnProperty); }
      private set { SetProperty(CreatedOnProperty, value); }
    }

    private static readonly PropertyInfo<DateTime> LastChangedProperty = RegisterProperty<DateTime>(c => c.LastChanged);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedProperty")]
    public DateTime LastChanged
    {
      get { return GetProperty(LastChangedProperty); }
      private set { SetProperty(LastChangedProperty, value); }
    }

    private static readonly PropertyInfo<PersonInfo> LastChangedByProperty = RegisterProperty<PersonInfo>(c => c.LastChangedBy);
    [Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(ValidationErrors), ErrorMessageResourceName = @"LastChangedByRequiredPropertyError")]
    [Display(ResourceType = typeof(PropertiesNames), Name = @"LastChangedByProperty")]
    public PersonInfo LastChangedBy
    {
      get { return GetProperty(LastChangedByProperty); }
      private set { SetProperty(LastChangedByProperty, value); }
    }

    private static readonly PropertyInfo<byte[]> TimeStampProperty = RegisterProperty<byte[]>(c => c.TimeStamp);
    private byte[] TimeStamp
    {
      get { return GetProperty(TimeStampProperty); }
      set { SetProperty(TimeStampProperty, value); }
    }

    #endregion

    internal void LoadData(Participant participantDB)
    {
      if (participantDB == null) return;
      ParticipantId = participantDB.ParticipantId;
      Person = new PersonInfo(participantDB.PersonRef);
      Role = new BusinessRoleInfo(participantDB.BusinessRoleRef);

      // Common properties
      CreatedBy = new PersonInfo(participantDB.CreatedByRef);
      LastChangedBy = new PersonInfo(participantDB.LastChangedByRef);

      CreatedOn = participantDB.CreatedOn;
      LastChanged = participantDB.LastChanged;

      IsDeletedField = participantDB.IsDeleted;
      TimeStamp = participantDB.TimeStamp.ToArray();
    }

    #endregion

    #region Business Rules

    protected override void AddBusinessRules()
    {
      base.AddBusinessRules();
      BusinessRules.AddRule(new PersonCantBeEmpty { PrimaryProperty = PersonProperty });
    }

    private class PersonCantBeEmpty : BusinessRule
    {
      protected override void Execute(RuleContext context)
      {
        var target = (ParticipantBO)context.Target;
        var person = target.ReadProperty(PersonProperty);
        if (person != null && person.IsEmpty())
          context.AddErrorResult(ValidationErrors.ParticipantPersonRequiredPropertyError);
      }
    }

    #endregion

    #region  Data Access

    private void Child_Create(ActivityBO activity)
    {
      if (activity == null) throw new ArgumentNullException(ExceptionsMessages.ActivityNullReference);
      var currentUser = SecurityChecks.GetCurrentUser();
      using (BypassPropertyChecks)
      {
        ParticipantId = Guid.NewGuid();
        ActivityId = activity.ActivityId;
        CreatedBy = new PersonInfo(currentUser);
        LastChangedBy = new PersonInfo(currentUser);
        CreatedOn = DateTime.Now;
        Person = new PersonInfo(PersonBO.GetEmptyPerson());
        Role = new BusinessRoleInfo(BusinessRoleBO.GetEmptyRole());
      }
      base.Child_Create();
    }

    private void Child_Fetch(Participant data, ActivityBO activity)
    {
      if (activity == null) throw new ArgumentNullException(ExceptionsMessages.ActivityNullReference);
      using (BypassPropertyChecks)
      {
        ActivityId = activity.ActivityId;
        LoadData(data);
      }
    }

    private void Child_Insert()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        Binary lastTimeStamp = null;
        LastChanged = DateTime.Now;
        using (BypassPropertyChecks)
        {
          ctx.DataContext.AddParticipant(
            ParticipantId,
            ActivityId,
            Person == null || Person.IsEmpty() ? (Guid?)null : Person.Id,
            Role == null || Role.IsEmpty() ? (Guid?)null : Role.Id,
            CreatedBy.IsEmpty() ? (Guid?)null : CreatedBy.Id,
            IsDeletedField,
            CreatedOn,
            LastChanged,
            LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
            ref lastTimeStamp);
          TimeStamp = lastTimeStamp.ToArray();
        }
      }
    }

    private void Child_Update()
    {
      using (var ctx = ContextManager<BusinessControl2DataContext>.GetManager(Database.BizControlDB))
      {
        if (IsSelfDirty)
        {
          Binary lastTimeStamp = null;
          LastChanged = DateTime.Now;
          LastChangedBy = new PersonInfo(SecurityChecks.GetCurrentUser());
          using (BypassPropertyChecks)
          {
            ctx.DataContext.UpdateParticipant(
              ParticipantId,
              ActivityId,
              Person == null || Person.IsEmpty() ? (Guid?)null : Person.Id,
              Role == null || Role.IsEmpty() ? (Guid?)null : Role.Id,
              IsDeletedField,
              LastChanged,
              LastChangedBy.IsEmpty() ? (Guid?)null : LastChangedBy.Id,
              TimeStamp,
              ref lastTimeStamp);
            TimeStamp = lastTimeStamp.ToArray();
          }
        }
      }
    }

    #endregion

    #region  Factory Methods

    public static ParticipantBO NewParticipant(ActivityBO activity)
    {
      return Csla.DataPortal.CreateChild<ParticipantBO>(activity);
    }

    public static ParticipantBO GetParticipant(Participant data, ActivityBO activity)
    {
      return Csla.DataPortal.FetchChild<ParticipantBO>(data, activity);
    }
    
    private static ParticipantBO _emptyParticipant;
    public static ParticipantBO GetEmptyParticipant()
    {
      if (_emptyParticipant == null)
      {
        _emptyParticipant = new ParticipantBO();
        _emptyParticipant.ParticipantId = Guid.Empty;
        _emptyParticipant.Role = new BusinessRoleInfo(BusinessRoleBO.GetEmptyRole());
        _emptyParticipant.Person = new PersonInfo(PersonBO.GetEmptyPerson());
      }
      return _emptyParticipant;
    }

    private ParticipantBO()
    { /* require use of factory methods */ }

    #endregion  
  
    #region ISupportEmtpyValue Members

    public bool IsEmpty()
    {
      return ParticipantId == Guid.Empty;
    }

    #endregion
  }
}
